#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
struct Node
{
  char *val;
  struct Node *next;
  struct Node *priv;
};

struct Node *make_node(char *val, struct Node *next, struct Node *priv)
{
  struct Node *node = malloc(sizeof(struct Node));
  node->val = val;
  node->next = next;
  node->priv = priv;
  return node;
}

void free_node(struct Node *node)
{
  free(node->val);
  free(node);
}

void free_list(struct Node *node)
{
  if (node == NULL)
    return;
  else
  {
    struct Node *tmp = node->next;
    free_node(node);
    free_list(tmp);
  }
}

int main(int argc, char *const argv[])
{
  if (argc > 3)
  {
    fprintf(stderr, "usage: reverse <input> <output>\n");
    exit(EXIT_FAILURE);
  }
  FILE *in, *out;
  if (argc == 1)
  {
    in = stdin;
    out = stdout;
  }
  if (argc == 2)
  {
    in = fopen(argv[1], "r");
    out = stdout;
  }
  if (argc == 3)
  {
    if (strcmp(argv[1], argv[2]) == 0)
    {
      fprintf(stderr, "reverse: input and output file must differ\n");
      exit(EXIT_FAILURE);
    }
    int ind, outd;
    struct stat instate;
    struct stat outstate;
    ind = open(argv[1], O_RDONLY);
    outd = open(argv[2], O_RDONLY);
    if (ind < -1)
    {
      fprintf(stderr, "reverse: cannot open file '%s'\n", argv[1]);
      exit(EXIT_FAILURE);
    }
    if (outd < -1)
    {
      fprintf(stderr, "reverse: cannot open file '%s'\n", argv[2]);
      exit(EXIT_FAILURE);
    }
    fstat(ind, &instate);
    fstat(outd, &outstate);
    if (instate.st_ino == outstate.st_ino)
    {
      fprintf(stderr, "reverse: input and output file must differ\n");
      exit(EXIT_FAILURE);
    }
    close(ind);
    close(outd);
    in = fopen(argv[1], "r");
    out = fopen(argv[2], "w");
  }

  if (in == NULL)
  {
    fprintf(stderr, "reverse: cannot open file '%s'\n", argv[1]);
    exit(EXIT_FAILURE);
  }

  if (out == NULL)
  {
    fprintf(stderr, "reverse: cannot open file '%s'\n", argv[2]);
    exit(EXIT_FAILURE);
  }

  struct Node *const head = make_node(NULL, NULL, NULL);
  struct Node *tail = head; // tail should point to a null space

  char *line = NULL;
  size_t len = 0;
  ssize_t nread = 0;
  while ((nread = getline(&line, &len, in)) != -1)
  {
    tail->val = calloc(sizeof(char), nread);
    strcpy(tail->val, line);
    struct Node *tmp = malloc(sizeof(struct Node));
    tail->next = tmp;
    tmp->priv = tail;
    tail = tmp;
  }
  free(line);
  tail = tail->priv;
  while (tail != head)
  {
    fprintf(out, "%s", tail->val);
    tail = tail->priv;
  }
  fprintf(out, "%s", tail->val);
  free_list(head);
  exit(EXIT_SUCCESS);
}